/*****************************************************************************
 * hal_efm32lg_uart.c
 *
 * Copyright (C) 2019 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/

#include "plum_api.h"
#include "hal/source/hal_efm32lg/hal_efm32lg.h"
#include "thirdparty/fifo/fifo.h"
/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/

#define LOG_RUN_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE    "hal.uart"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef union {
    USART_TypeDef * uart_hdl;
    LEUART_TypeDef *leuart_hdl;
} uart_hdl;

typedef struct {
    union {
        USART_TypeDef * uart_hdl;
        LEUART_TypeDef *leuart_hdl;
    } hdl;
    fifo_t  fifo;
    plum_u8 rxbyte;

    struct list_head list;
} hal_efm32_uart_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

PLUM_PRIVATE
hal_efm32_uart_t uart_head;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
USART_TypeDef *hal_efm32_uart_hdl(plum_u32 id)
{
    USART_TypeDef *uart = plum_null;

    switch (PLUM_HAL_UART_IDX(id)) {
#if defined(USART0)
        case 0:
            uart = USART0;
            break;
#endif
#if defined(USART1)
        case 1:
            uart = USART1;
            break;
#endif
#if defined(USART2)
        case 2:
            uart = USART2;
            break;
#endif
#if defined(UART0)
        case 10:
            uart = UART0;
            break;
#endif
#if defined(UART1)
        case 11:
            uart = UART1;
            break;
#endif
#if defined(UART2)
        case 12:
            uart = UART2;
            break;
#endif
        default:
            break;
    }

    return (uart);
}

PLUM_PRIVATE
LEUART_TypeDef *hal_efm32_leuart_hdl(plum_u32 id)
{
    LEUART_TypeDef *uart = plum_null;

    switch (PLUM_HAL_UART_IDX(id)) {
#if defined(LEUART0)
        case 100:
            uart = LEUART0;
            break;
#endif

#if defined(LEUART1)
        case 101:
            uart = LEUART1;
            break;
#endif
        default:
            break;
    }

    return (uart);
}

PLUM_PRIVATE
plum_void hal_efm32_uart_clock_enable(plum_u32 id)
{
    switch (PLUM_HAL_UART_IDX(id)) {
#if defined(CMU_HFPERCLKEN0_USART0)
        case 0:
            CMU_ClockEnable(cmuClock_USART0, true);
            LOG_D("USART0 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_UART0)
        case 10:
            CMU_ClockEnable(cmuClock_UART0, true);
            LOG_D("UART0 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_USART1)
        case 1:
            CMU_ClockEnable(cmuClock_USART1, true);
            LOG_D("USART1 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_UART1)
        case 11:
            CMU_ClockEnable(cmuClock_UART1, true);
            LOG_D("UART1 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_USART2)
        case 2:
            CMU_ClockEnable(cmuClock_USART2, true);
            LOG_D("USART2 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_UART2)
        case 12:
            CMU_ClockEnable(cmuClock_UART2, true);
            LOG_D("USART2 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_USART3)
        case 3:
            CMU_ClockEnable(cmuClock_USART3, true);
            LOG_D("USART3 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_UART3)
        case 13:
            CMU_ClockEnable(cmuClock_UART3, true);
            LOG_D("UART3 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_USART4)
        case 4:
            CMU_ClockEnable(cmuClock_USART4, true);
            LOG_D("USART4 CLK enable");
            break;
#endif
#if defined(CMU_HFPERCLKEN0_UART4)
        case 14:
            CMU_ClockEnable(cmuClock_UART4, true);
            LOG_D("UART3 CLK enable");
            break;
#endif
#if defined(CMU_LFBCLKEN0_LEUART0)
        case 100:
            CMU_ClockEnable(cmuClock_LEUART0, true);
            LOG_D("LEUART0 CLK enable");
            break;
#endif
#if defined(CMU_LFBCLKEN0_LEUART1)
        case 101:
            LOG_D("LEUART1 CLK enable");
            CMU_ClockEnable(cmuClock_LEUART1, true);
            break;
#endif
        default:
            break;
    }
}

#if defined(CMU_HFPERCLKEN0_USART0)
PLUM_PRIVATE
plum_void hal_efm32_usart0_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(E, 10) == tx_id) &&
        (PLUM_HAL_GPIO_ID(E, 11) == rx_id)) {
        LOG_D("USART0 #0");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(E, 7) == tx_id) &&
             (PLUM_HAL_GPIO_ID(E, 6) == rx_id)) {
        LOG_D("USART0 #1");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC1;
    }
    else if ((PLUM_HAL_GPIO_ID(C, 11) == tx_id) &&
             (PLUM_HAL_GPIO_ID(C, 10) == rx_id)) {
        LOG_D("USART0 #2");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC2;
    }
    else if ((PLUM_HAL_GPIO_ID(E, 13) == tx_id) ||
             (PLUM_HAL_GPIO_ID(E, 12) == rx_id)) {
        LOG_D("USART0 #3");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC3;
    }
    else if ((PLUM_HAL_GPIO_ID(B, 7) == tx_id) &&
             (PLUM_HAL_GPIO_ID(B, 8) == rx_id)) {
        LOG_D("USART0 #4");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC4;
    }
    else if ((PLUM_HAL_GPIO_ID(C, 0) == tx_id) &&
             (PLUM_HAL_GPIO_ID(C, 1) == rx_id)) {
        LOG_D("USART0 #5");
        USART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC5;
    }
}
#endif
#if defined(CMU_HFPERCLKEN0_UART0)
PLUM_PRIVATE
plum_void hal_efm32_uart0_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(F, 6) == tx_id) &&
        (PLUM_HAL_GPIO_ID(F, 7) == rx_id)) {
        LOG_D("UART0 #0");
        UART0->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(E, 0) == tx_id) &&
             (PLUM_HAL_GPIO_ID(E, 1) == rx_id)) {
        LOG_D("UART0 #1");
        UART0->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC1;
    }
    else if ((PLUM_HAL_GPIO_ID(A, 3) == tx_id) &&
             (PLUM_HAL_GPIO_ID(A, 4) == rx_id)) {
        LOG_D("UART0 #2");
        UART0->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC2;
    }
    else if ((PLUM_HAL_GPIO_ID(C, 14) == tx_id) &&
             (PLUM_HAL_GPIO_ID(C, 15) == rx_id)) {
        LOG_D("UART0 #3");
        UART0->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC3;
    }
}
#endif

#if defined(CMU_HFPERCLKEN0_USART1)
PLUM_PRIVATE
plum_void hal_efm32_usart1_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(C, 0) == tx_id) &&
        (PLUM_HAL_GPIO_ID(C, 1) == rx_id)) {
        LOG_D("USART1 #0");
        USART1->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(D, 0) == tx_id) &&
             (PLUM_HAL_GPIO_ID(D, 1) == rx_id)) {
        LOG_D("USART1 #1");
        USART1->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC1;
    }
    else if ((PLUM_HAL_GPIO_ID(D, 7) == tx_id) &&
             (PLUM_HAL_GPIO_ID(D, 6) == rx_id)) {
        LOG_D("USART1 #2");
        USART1->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC2;
    }
}
#endif

#if defined(CMU_HFPERCLKEN0_UART1)
PLUM_PRIVATE
plum_void hal_efm32_uart1_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(C, 12) == tx_id) &&
        (PLUM_HAL_GPIO_ID(C, 13) == rx_id)) {
        LOG_D("UART1 #0");
        UART1->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(F, 10) == tx_id) &&
             (PLUM_HAL_GPIO_ID(F, 11) == rx_id)) {
        LOG_D("UART1 #1");
        UART1->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC1;
    }
    else if ((PLUM_HAL_GPIO_ID(B, 9) == tx_id) &&
             (PLUM_HAL_GPIO_ID(B, 10) == rx_id)) {
        LOG_D("UART1 #2");
        UART1->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC2;
    }
    else if ((PLUM_HAL_GPIO_ID(E, 2) == tx_id) &&
             (PLUM_HAL_GPIO_ID(E, 3) == rx_id)) {
        LOG_D("UART1 #3");
        UART1->ROUTE |=
            UART_ROUTE_TXPEN | UART_ROUTE_RXPEN | UART_ROUTE_LOCATION_LOC3;
    }
}
#endif
#if defined(CMU_HFPERCLKEN0_USART2)
PLUM_PRIVATE
plum_void hal_efm32_usart2_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(C, 2) == tx_id) &&
        (PLUM_HAL_GPIO_ID(C, 3) == rx_id)) {
        LOG_D("USART2 #0");
        USART2->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;
    }
    if ((PLUM_HAL_GPIO_ID(B, 3) == tx_id) &&
        (PLUM_HAL_GPIO_ID(B, 4) == rx_id)) {
        LOG_D("USART2 #1");
        USART2->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC1;
    }
}
#endif
#if defined(CMU_HFPERCLKEN0_UART2)
PLUM_PRIVATE
plum_void hal_efm32_uart2_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
}
#endif
#if defined(CMU_LFBCLKEN0_LEUART0)
PLUM_PRIVATE
plum_void hal_efm32_leuart0_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(D, 4) == tx_id) &&
        (PLUM_HAL_GPIO_ID(D, 5) == rx_id)) {
        LOG_D("LEUART0 #0");
        LEUART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(B, 13) == tx_id) &&
             (PLUM_HAL_GPIO_ID(B, 14) == rx_id)) {
        LOG_D("LEUART0 #1");
        LEUART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC1;
    }
    if ((PLUM_HAL_GPIO_ID(E, 14) == tx_id) &&
        (PLUM_HAL_GPIO_ID(E, 15) == rx_id)) {
        LOG_D("LEUART0 #2");
        LEUART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC2;
    }
    else if ((PLUM_HAL_GPIO_ID(F, 0) == tx_id) &&
             (PLUM_HAL_GPIO_ID(F, 1) == rx_id)) {
        LOG_D("LEUART0 #3");
        LEUART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC3;
    }
    else if ((PLUM_HAL_GPIO_ID(F, 2) == tx_id) &&
             (PLUM_HAL_GPIO_ID(A, 0) == rx_id)) {
        LOG_D("LEUART0 #4");
        LEUART0->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC4;
    }
}
#endif

#if defined(CMU_LFBCLKEN0_LEUART1)
PLUM_PRIVATE
plum_void hal_efm32_leuart1_channel_enable(plum_u32 rx_id, plum_u32 tx_id)
{
    if ((PLUM_HAL_GPIO_ID(C, 6) == tx_id) &&
        (PLUM_HAL_GPIO_ID(C, 7) == rx_id)) {
        LOG_D("LEUART1 #0");
        LEUART1->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;
    }
    else if ((PLUM_HAL_GPIO_ID(A, 5) == tx_id) &&
             (PLUM_HAL_GPIO_ID(A, 6) == rx_id)) {
        LOG_D("LEUART1 #1");
        LEUART1->ROUTE |=
            USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC1;
    }
}
#endif

PLUM_PRIVATE
plum_void hal_efm32_uart_channel(plum_u32 uart_id, plum_u32 rx_id,
                                 plum_u32 tx_id)
{
    switch (PLUM_HAL_UART_IDX(uart_id)) {
        case 0:
            NVIC_ClearPendingIRQ(USART0_RX_IRQn);  //清除uart0接收等待中断
            NVIC_ClearPendingIRQ(USART0_TX_IRQn);  //清除uart0发送等待中断
            NVIC_EnableIRQ(USART0_RX_IRQn);        //中断控制器
            NVIC_EnableIRQ(USART0_TX_IRQn);
            NVIC_SetPriority(USART0_RX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            NVIC_SetPriority(USART0_TX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            LOG_D("uart nvic success");

            hal_efm32_usart0_channel_enable(rx_id, tx_id);
            LOG_D("USART0 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;
        case 10:
            NVIC_ClearPendingIRQ(UART0_RX_IRQn);  //清除uart0接收等待中断
            NVIC_ClearPendingIRQ(UART0_TX_IRQn);  //清除uart0发送等待中断
            NVIC_EnableIRQ(UART0_RX_IRQn);        //中断控制器
            NVIC_EnableIRQ(UART0_TX_IRQn);
            NVIC_SetPriority(UART0_RX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            NVIC_SetPriority(UART0_TX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            LOG_D("uart nvic success");

            hal_efm32_uart0_channel_enable(rx_id, tx_id);
            LOG_D("UART0 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;
        case 1:
            NVIC_ClearPendingIRQ(USART1_RX_IRQn);  //清除uart0接收等待中断
            NVIC_ClearPendingIRQ(USART1_TX_IRQn);  //清除uart0发送等待中断
            NVIC_EnableIRQ(USART1_RX_IRQn);        //中断控制器
            NVIC_EnableIRQ(USART1_TX_IRQn);
            NVIC_SetPriority(USART1_RX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            NVIC_SetPriority(USART1_TX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            LOG_D("uart nvic success");

            hal_efm32_usart1_channel_enable(rx_id, tx_id);
            LOG_D("USART1 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;
        case 11:
            NVIC_ClearPendingIRQ(UART1_RX_IRQn);  //清除uart0接收等待中断
            NVIC_ClearPendingIRQ(UART1_TX_IRQn);  //清除uart0发送等待中断
            NVIC_EnableIRQ(UART1_RX_IRQn);        //中断控制器
            NVIC_EnableIRQ(UART1_TX_IRQn);
            NVIC_SetPriority(UART1_RX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            NVIC_SetPriority(UART1_TX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            LOG_D("uart nvic success");

            hal_efm32_uart1_channel_enable(rx_id, tx_id);
            LOG_D("UART1 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;
        case 2:
            NVIC_ClearPendingIRQ(USART2_RX_IRQn);  //清除uart0接收等待中断
            NVIC_ClearPendingIRQ(USART2_TX_IRQn);  //清除uart0发送等待中断
            NVIC_EnableIRQ(USART2_RX_IRQn);        //中断控制器
            NVIC_EnableIRQ(USART2_TX_IRQn);
            NVIC_SetPriority(USART2_RX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            NVIC_SetPriority(USART2_TX_IRQn, CORE_INTERRUPT_HIGHEST_PRIORITY);
            LOG_D("uart nvic success");

            hal_efm32_usart2_channel_enable(rx_id, tx_id);
            LOG_D("USART2 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;

        case 100:
            NVIC_ClearPendingIRQ(LEUART0_IRQn);  //清除uart0接收等待中断
            NVIC_EnableIRQ(LEUART0_IRQn);        //中断控制器
            LOG_D("leuart nvic success");

            hal_efm32_leuart0_channel_enable(rx_id, tx_id);
            LOG_D("LEUART0 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;

        case 101:
            NVIC_ClearPendingIRQ(LEUART1_IRQn);  //清除uart0接收等待中断
            NVIC_EnableIRQ(LEUART1_IRQn);        //中断控制器
            LOG_D("leuart nvic success");

            hal_efm32_leuart1_channel_enable(rx_id, tx_id);
            LOG_D("LEUART1 RX(0x%08x) TX(0X%08X)", rx_id, tx_id);
            break;

        default:
            break;
    }
}

PLUM_PRIVATE
USART_Databits_TypeDef hal_efm32_uart_databits(plum_hal_uart_databit_t cof)
{
    USART_Databits_TypeDef databit = usartDatabits16;

    switch (cof) {
        case PLUM_HAL_UART_DATABIT_5:
            databit = usartDatabits5;
            break;
        case PLUM_HAL_UART_DATABIT_6:
            databit = usartDatabits6;
            break;
        case PLUM_HAL_UART_DATABIT_7:
            databit = usartDatabits7;
            break;
        case PLUM_HAL_UART_DATABIT_8:
            databit = usartDatabits8;
            break;
        case PLUM_HAL_UART_DATABIT_9:
            databit = usartDatabits9;
            break;
        default:
            break;
    }

    return (databit);
}

PLUM_PRIVATE
LEUART_Databits_TypeDef hal_efm32_leuart_databits(plum_hal_uart_databit_t cof)
{
    LEUART_Databits_TypeDef databit = leuartDatabits8;

    switch (cof) {
        case PLUM_HAL_UART_DATABIT_8:
            databit = leuartDatabits8;
            break;

        case PLUM_HAL_UART_DATABIT_9:
            databit = leuartDatabits9;
            break;

        default:
            LOG_E("leuart not support current databit : %d", cof);
            break;
    }

    return (databit);
}

PLUM_PRIVATE
USART_Parity_TypeDef hal_efm32_uart_parity(plum_hal_uart_parity_t cof)
{
    USART_Parity_TypeDef parity = usartNoParity;

    switch (cof) {
        case PLUM_HAL_UART_PARITY_NONE:
            parity = usartNoParity;
            break;
        case PLUM_HAL_UART_PARITY_EVEN:
            parity = usartEvenParity;
            break;
        case PLUM_HAL_UART_PARITY_ODD:
            parity = usartOddParity;
            break;
        default:
            break;
    }

    return (parity);
}

PLUM_PRIVATE
LEUART_Parity_TypeDef hal_efm32_leuart_parity(plum_hal_uart_parity_t cof)
{
    LEUART_Parity_TypeDef parity = leuartNoParity;

    switch (cof) {
        case PLUM_HAL_UART_PARITY_NONE:
            parity = leuartNoParity;
            break;
        case PLUM_HAL_UART_PARITY_EVEN:
            parity = leuartEvenParity;
            break;
        case PLUM_HAL_UART_PARITY_ODD:
            parity = leuartOddParity;
            break;
        default:
            break;
    }

    return (parity);
}

PLUM_PRIVATE
USART_Stopbits_TypeDef hal_efm32_uart_stopbits(plum_hal_uart_stopbit_t cof)
{
    USART_Stopbits_TypeDef stopbits = usartStopbits0p5;

    switch (cof) {
        case PLUM_HAL_UART_STOPBIT_0_5:
            stopbits = usartStopbits0p5;
            break;
        case PLUM_HAL_UART_STOPBIT_1:
            stopbits = usartStopbits1;
            break;
        case PLUM_HAL_UART_STOPBIT_1_5:
            stopbits = usartStopbits1p5;
            break;
        case PLUM_HAL_UART_STOPBIT_2:
            stopbits = usartStopbits2;
            break;
        default:
            break;
    }
    return (stopbits);
}

PLUM_PRIVATE
LEUART_Stopbits_TypeDef hal_efm32_leuart_stopbits(plum_hal_uart_stopbit_t cof)
{
    LEUART_Stopbits_TypeDef stopbits = leuartStopbits1;

    switch (cof) {
        case PLUM_HAL_UART_STOPBIT_1:
            stopbits = leuartStopbits1;
            break;
        case PLUM_HAL_UART_STOPBIT_2:
            stopbits = leuartStopbits2;
            break;
        default:
            break;
    }
    return (stopbits);
}

PLUM_PRIVATE
hal_efm32_uart_t *hal_efm32_uart_apply(plum_u32 id, plum_u32 rxsize)
{
    hal_efm32_uart_t *node = NULL;

    do {
        if (!uart_head.list.next) {
            INIT_LIST_HEAD(&uart_head.list);
        }

        node =
            (hal_efm32_uart_t *)plum_kal_mem_malloc(sizeof(hal_efm32_uart_t));
        if (!node) {
            break;
        }

        if (PLUM_HAL_UART_ID(id) < 100) {
            node->hdl.uart_hdl = hal_efm32_uart_hdl(id);
        }
        else {
            node->hdl.leuart_hdl = hal_efm32_leuart_hdl(id);
        }

        node->fifo = fifo_init(rxsize);
        if (!node->fifo) {
            LOG_E("fifo_init err");
            PLUM_FREE(node);
            node = NULL;
        }
    } while (0);

    return (node);
}

PLUM_PRIVATE
hal_efm32_uart_t *hal_efm32_uart_find(plum_u32 id)
{
    hal_efm32_uart_t *node = NULL;

    struct list_head *pos;
    list_for_each(pos, &uart_head.list)
    {
        node = list_entry(pos, hal_efm32_uart_t, list);
        if ((node->hdl.uart_hdl == hal_efm32_uart_hdl(id)) ||
            (node->hdl.leuart_hdl == hal_efm32_leuart_hdl(id))) {
            LOG_D("find the node ,id: 0x%08X", id);
            break;
        }
    }

    return (node);
}

PLUM_PRIVATE
plum_s32 hal_efm32_uart_insert(hal_efm32_uart_t *node)
{
    plum_s32 rc = PLUM_ECODE_EPARA;

    if (node) {
        list_add(&node->list, &uart_head.list);
        rc = PLUM_ECODE_OK;
    }

    return (rc);
}

PLUM_PRIVATE
plum_s32 hal_efm32_uart_del(hal_efm32_uart_t *node)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!node) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        list_del(&node->list);
        fifo_deinit(node->fifo);

        PLUM_FREE(node);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 hal_efm32_usart_init(plum_u32 id, plum_hal_uart_cof_t *config)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        USART_TypeDef *uart = hal_efm32_uart_hdl(id);
        if (!uart) {
            LOG_E("not find the uart hdl");
            rc = PLUM_ECODE_EPARA;
            break;
        }
        LOG_I("find usart handle");

        USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT;
        init.enable                  = usartEnable;
        init.baudrate                = config->baudrate;
        init.databits                = hal_efm32_uart_databits(config->databit);
        init.parity                  = hal_efm32_uart_parity(config->parity);
        init.stopbits                = hal_efm32_uart_stopbits(config->stopbit);
        USART_InitAsync(uart, &init);
        LOG_D("USART_InitAsync success");

        USART_Enable(uart, usartEnable);
        LOG_D("uart enable success");

        USART_IntClear(uart, _USART_IFC_MASK);     //清除中断标志
        USART_IntEnable(uart, USART_IEN_RXDATAV);  //使能接收中断

        //选择通道
        hal_efm32_uart_channel(id, config->rxpin_id, config->txpin_id);

        hal_efm32_uart_t *node = hal_efm32_uart_apply(id, config->rxbuf_size);
        if (!node) {
            LOG_E("hal_efm32_uart_apply err");
            rc = PLUM_ECODE_EMEM;
            break;
        }
        node->hdl.uart_hdl = uart;

        rc = hal_efm32_uart_insert(node);
        if (rc) {
            LOG_E("hal_efm32_uart_insert err,rc:%d", rc);
            break;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 hal_efm32_leuart_init(plum_u32 id, plum_hal_uart_cof_t *config)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        LEUART_TypeDef *uart = hal_efm32_leuart_hdl(id);
        if (!uart) {
            rc = PLUM_ECODE_EPARA;
            break;
        }
        LOG_I("find leuart handle");

        LEUART_Init_TypeDef init = LEUART_INIT_DEFAULT;
        init.enable              = leuartEnable;
        init.baudrate            = config->baudrate;
        init.databits            = hal_efm32_leuart_databits(config->databit);
        init.parity              = hal_efm32_leuart_parity(config->parity);
        init.stopbits            = hal_efm32_leuart_stopbits(config->stopbit);
        LEUART_Init(uart, &init);
        LOG_D("LEUART_Init success");
    } while (0);

    return (rc);
}

/*****************************************************************************
 * Irq Functions
 ****************************************************************************/

#if defined(CMU_HFPERCLKEN0_UART0)
void UART0_RX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(UART0);
    USART_IntClear(UART0, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(0));
    if (!node) {
        LOG_E("uart 0 not init ");
        return;
    }

    plum_u8 byte = USART_Rx(UART0);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}

void UART0_TX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(UART0);
    USART_IntClear(UART0, flag);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_HFPERCLKEN0_USART0)
void USART0_RX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART0);
    USART_IntClear(USART0, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(0));
    if (!node) {
        LOG_E("usart 0 not init ");
        return;
    }

    plum_u8 byte = USART_Rx(USART0);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}

void USART0_TX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART0);
    USART_IntClear(USART0, flag);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_HFPERCLKEN0_UART1)
void UART1_RX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(UART1);
    USART_IntClear(UART1, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(11));
    if (!node) {
        LOG_E("uart 1 not init ");
        return;
    }

    plum_u8 byte = USART_Rx(UART1);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}

void UART1_TX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(UART1);
    USART_IntClear(UART1, flag);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_HFPERCLKEN0_USART1)
void USART1_RX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART1);
    USART_IntClear(USART1, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(1));
    if (!node) {
        LOG_E("usart 1 not init ");
        return;
    }

    plum_u8 byte = USART_Rx(USART1);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}

void USART1_TX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART1);
    USART_IntClear(USART1, flag);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_HFPERCLKEN0_USART2)
void USART2_RX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART2);
    USART_IntClear(USART2, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(2));
    if (!node) {
        LOG_E("usart 2 not init ");
        return;
    }

    plum_u8 byte = USART_Rx(USART2);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}

void USART2_TX_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = USART_IntGet(USART2);
    USART_IntClear(USART2, flag);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_LFBCLKEN0_LEUART0)
void LEUART0_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = LEUART_IntGet(LEUART0);
    LEUART_IntClear(LEUART0, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(100));
    if (!node) {
        LOG_E("LEUART0 not init ");
        return;
    }

    plum_u8 byte = LEUART_Rx(LEUART0);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}
#endif

#if defined(CMU_LFBCLKEN0_LEUART1)
void LEUART1_IRQHandler(void)
{
    plum_u32 level = plum_kal_critical_enter();

    plum_u32 flag = LEUART_IntGet(LEUART1);
    LEUART_IntClear(LEUART1, flag);

    hal_efm32_uart_t *node = hal_efm32_uart_find(PLUM_HAL_UART_ID(101));
    if (!node) {
        LOG_E("LEUART0 not init ");
        return;
    }

    plum_u8 byte = LEUART_Rx(LEUART1);
    fifo_write(node->fifo, &byte, 1);

    plum_kal_critical_exit(level);
}
#endif

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_hal_uart_init(plum_u32 id, plum_hal_uart_cof_t *config)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        // steps_1: 时钟
        hal_efm32_uart_clock_enable(id);
        CMU_ClockEnable(cmuClock_HFPER, true);
        CMU_ClockEnable(cmuClock_GPIO, true);
        CMU_ClockEnable(cmuClock_HFLE, true);

        // steps_2：GPIO
        plum_hal_gpio_cof_t gpioconf = {
            .mode       = PLUM_HAL_GPIO_MODE_COMMON,
            .config.com = PLUM_HAL_GPIO_MODE_OUT_PP,
        };
        rc = plum_hal_gpio_init(config->txpin_id, &gpioconf);
        if (rc) {
            LOG_E("plum_hal_gpio_init err,rc:%d", rc);
            break;
        }

        gpioconf.config.com = PLUM_HAL_GPIO_MODE_INPUT;
        rc                  = plum_hal_gpio_init(config->rxpin_id, &gpioconf);
        if (rc) {
            LOG_E("plum_hal_gpio_init err,rc:%d", rc);
            break;
        }

        // steps_3：配置
        if (PLUM_HAL_UART_IDX(id) < 100) {
            LOG_D("hal_efm32_usart_init");
            rc = hal_efm32_usart_init(id, config);
        }
        else {
            LOG_D("hal_efm32_leuart_init");
            rc = hal_efm32_leuart_init(id, config);
        }
        if (rc) {
            LOG_E("hal_efm32_uart init err,rc:%d", rc);
            break;
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_uart_send(plum_u32 id, const plum_void *data, plum_u16 cnt)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            rc = PLUM_ECODE_INVALID;
            break;
        }

        hal_efm32_uart_t *node = hal_efm32_uart_find(id);
        if (!node) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        plum_u16 loop;
        uint8_t  byte = 0;
        uint8_t *ptr  = data;
        for (loop = 0; loop < cnt; loop++) {
            byte = ptr[loop];
            if (PLUM_HAL_UART_IDX(id) < 100) {
                USART_Tx(node->hdl.uart_hdl, byte);
            }
            else {
                LEUART_Tx(node->hdl.leuart_hdl, *(uint8_t *)data + loop);
            }
        }
        rc = loop;
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_uart_recv(plum_u32 id, void *data, plum_u16 size,
                            plum_u32 timeout)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        hal_efm32_uart_t *node = hal_efm32_uart_find(id);
        if (!node) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        plum_u32 cur = 0, last = 0;
        while (timeout--) {
            cur = fifo_payload(node->fifo);
            if (cur && (cur == last)) {
                rc = cur;
                break;
            }
            last = cur;
            plum_kal_thread_delay(1);
        }

        rc = fifo_read(node->fifo, data, cur);
    } while (0);

    return (rc);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
